package com.echat.serviceapigateway.zuul;

import com.echat.commonlib.util.StringUtil;
import com.echat.serviceapigateway.security.service.ServiceService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.util.StringUtils;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created by chenmohan on 2018/10/16
 *
 * @description 继承自SimpleRouteLocator,主要实现了从数据库动态加载网关的路由表.
 *              原来的方式是每30秒从properties中加载最新的路由表,现在改为从数据库加载路由表,并手动调用接口刷新路由表
 */
@Slf4j
public class CustomRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {

    ServiceService serviceService;

    private ZuulProperties properties;

    @Setter
    private boolean hearBeatRefresh = false;

    public CustomRouteLocator(String servletPath, ZuulProperties properties, ServiceService serviceService) {
        super(servletPath, properties);
        this.properties = properties;
        this.serviceService = serviceService;
    }

    @Override
    public void refresh() {
        if (hearBeatRefresh) {
            doRefresh();
        }
    }

    /**
     * 该对外暴露的接口服务使用的方法
     */
    public void manualRefresh() {
        doRefresh();
    }

    @Override
    protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulProperties.ZuulRoute> routeMap = new LinkedHashMap<>();
        //从properties中加载路由信息
        routeMap.putAll(super.locateRoutes());
        //从db中加载路由信息
        routeMap.putAll(locateRoutesFromDB());
        log.info("从db加载数据");
        //优化一下配置
        LinkedHashMap<String, ZuulProperties.ZuulRoute> values = new LinkedHashMap<>();
        for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : routeMap.entrySet()) {
            String path = entry.getKey();
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (StringUtils.hasText(this.properties.getPrefix())) {
                path = this.properties.getPrefix() + path;
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
            }
            log.info(" routesMap -- serviceId: " + entry.getValue() + " , path: " + entry.getValue().getServiceId());
            values.put(path, entry.getValue());
        }

        routeMap.clear();
        return values;
    }

    private Map<? extends String, ? extends ZuulProperties.ZuulRoute> locateRoutesFromDB() {
        Map<String, ZuulProperties.ZuulRoute> zuulRouteMap = serviceService.listAll().parallelStream()
                .collect(Collectors.toMap(e -> e.getServicePath(), e -> {
                    if (StringUtil.isEmpty(e.getServiceUri()) || StringUtil.isEmpty(e.getServiceName())) {
                        return null;
                    }
                    ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
                    zuulRoute.setServiceId(e.getServiceName());
                    zuulRoute.setId(e.getServiceName());
                    zuulRoute.setPath(e.getServicePath());
                    zuulRoute.setCustomSensitiveHeaders(true);
                    zuulRoute.setRetryable(true);
                    return zuulRoute;
                }));
        return zuulRouteMap;
    }
}
